package com.kai.kaispringmvc.context;

import com.kai.kaispringmvc.annotation.kaiAutowired;
import com.kai.kaispringmvc.annotation.kaiController;
import com.kai.kaispringmvc.annotation.kaiService;
import com.kai.kaispringmvc.xml.XMLparser;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author kai
 * @version 1.0
 * @date 2024/7/10
 * @Description
 */
@SuppressWarnings("all")
public class kaiwebApplicationContext {

    //定义属性，保存扫描包/子包的全路径
    private List<String> classFullpathList = new ArrayList<>();


    //定义属性ioc，存放反射生成后的bean对象 /controller/service
    public ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();


    public kaiwebApplicationContext() {
    }

    private String configLocation;//属性，表示spring容器配置文件

    public kaiwebApplicationContext(String configLocation) {
        this.configLocation = configLocation;
    }


    //编写方法，完成自己的spring容器的初始化
    public void init() {
        String basePackage = XMLparser.getBasePackage(configLocation.split(":")[1]);
        String[] basePackages = basePackage.split(",");


        if (basePackages.length > 0) {
            for (String aPackage : basePackages) {
                scanPackage(aPackage);
            }
        }
        System.out.println("classFullpathList=" + classFullpathList);
        executeinstance();

        //完成注入的bean对象的属性的装配
        excuteAutowired();
        System.out.println("装配后ioc容器" + ioc);
    }


    //创建方法，完成对包的扫描 -->io，容器，字符串处理
    public void scanPackage(String pack) {


        //得到包的绝对路径
        //通过类的加载器，得到指定的包的对应的  工作路径【绝对路径】
        //不能直接使用test
        //使用tomcat
        URL url =
                this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));

        String path = url.getFile();

        System.out.println("path=" + path);
        //在io中，把目录视为一个文件
        File file = new File(path);

        //遍历file文件，可能存在子包
        for (File f : file.listFiles()) {
            if (f.isDirectory())  //如果是一个目录，需要递归扫描
            {
                scanPackage(pack + "." + f.getName());

            } else {
                //这时扫描到的文件可能是.class也可能是其他
                //就是是.class也存在是否需要注入  是否加入@component注解
                //目前先把文件的全路径都保存进去，后续再处理
                String classpath = pack + "." + f.getName().replaceAll(".class", "");
                System.out.println(classpath);
                classFullpathList.add(classpath);
            }
        }


    }


    //编写方法，将扫描到的类，在满足条件的情况下，反射到ioc容器
    public void executeinstance() {
        //判断是否扫描到类
        if (classFullpathList.size() == 0) {//说明没有扫描到类，直接返回即可
            return;
        }

        //遍历classFullPathList进行反射
        for (String classfulllist : classFullpathList) {
            try {
                Class<?> aClass = Class.forName(classfulllist);

                //说明当前这个类有controller注解
                if (aClass.isAnnotationPresent(kaiController.class)) {

                    //得到类名首字母小写
                    String name = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                    ioc.put(name, aClass.newInstance());

                }//如果有其他注解，可以扩展
                else if (aClass.isAnnotationPresent(kaiService.class)) {

                    kaiService serviceannotation = aClass.getAnnotation(kaiService.class);

                    String beanName = serviceannotation.value();

                    if ("".equals(beanName)) {//说明没有指定value

                        //此处略有不同，可以通过接口名/类名【首字母小写】来注入ioc容器
                        //得到所有的接口的名称--》反射
                        Class<?>[] interfaces = aClass.getInterfaces();
                        Object instance = aClass.newInstance();
                        for (Class<?> anInterface : interfaces) {
                            String serviceName = anInterface.getSimpleName().substring(0, 1).toLowerCase() + anInterface.getSimpleName().substring(1);

                            ioc.put(serviceName, instance);

                        }


                    } else {
                        ioc.put(beanName, aClass.newInstance());
                    }


                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("ioc+" + ioc);


    }

    //编写方法，完成属性的自动住装配
    public void excuteAutowired() {
        //判断ioc有无要装配的对象
        if (ioc.isEmpty()) {

            return;//也可以抛出异常 throw new RuntimeException("ioc容器没有bean对象")

        }

        //遍历ioc容器中所有注入的bean对象，然后获取到bean的所有字段，判断是否需要装配
        //
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            String key = entry.getKey();
            Object bean = entry.getValue();
            //得到bean的所有字段/属性
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                //判断是否有autowired注解
                if (field.isAnnotationPresent(kaiAutowired.class)) {

                    //得到当前字段autowired,可能他还有其他注解
                    kaiAutowired autowired = field.getAnnotation(kaiAutowired.class);

                    String autowiredbeanName = autowired.value();
                    if (autowiredbeanName.equals("")) {
                        //如果@kaiAutowired(value="")没有值，按照默认规则
                        //即得到的字段的首字母小写
                        Class<?> fieldType = field.getType();
                        autowiredbeanName = fieldType.getSimpleName().substring(0, 1).toLowerCase() + fieldType.getSimpleName().substring(1);

                    }
                    //如果设置value，按照beanName进行装配
                    //从ioc容器中获取到bean
                    if (null == ioc.get(autowiredbeanName)) {
                        //说明指定的名字不在ioc容器中
                        throw new RuntimeException("ioc容器中，不存在该bean");

                    }
                    //属性可能是私有的 暴力反射
                    field.setAccessible(true);
                    //可以进行装配属性*****
                    try {
                        field.set(bean, ioc.get(autowiredbeanName));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }

                }


            }

        }

    }

}



